﻿using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using JT809.Protocol;
using JT809.Protocol.Configs;
using JT809.Protocol.Enums;
using JT809.Protocol.Extensions;
using JT809.Protocol.MessageBody;
using JTT809_2011_Service.DotNettyHandle;
using JTT809_2011_Service.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Polly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace JTT809_2011_Service.JT809
{
    /// <summary>
    /// 主链路客户端
    /// 针对企业与监管平台之间
    /// </summary>
    public sealed class JT809MainClient : IDisposable
    {
        private Bootstrap bootstrap;

        private MultithreadEventLoopGroup group;

        private IChannel channel;

        private bool disposed = false;

        private readonly ILogger<JT809MainClient> _logger;

        private readonly IServiceProvider _serviceProvider;

        private readonly IJT809ManualResetEvent _manualResetEvent;

        private readonly JT809HeaderOptions _JT809HeaderOptions;

        private readonly IPEndPointOptions _IPEndPointConfig;

        #region ctor
        public JT809MainClient
            (
            JT809HeaderOptions jT809HeaderOptions,
            IJT809ManualResetEvent jT809ManualResetEvent,
            IServiceProvider provider,
            ILoggerFactory loggerFactory,
            IPEndPointOptions iPEndPointConfig
            )
        {
            _JT809HeaderOptions = jT809HeaderOptions;
            this._serviceProvider = provider;
            this._logger = loggerFactory.CreateLogger<JT809MainClient>();
            this._manualResetEvent = jT809ManualResetEvent;
            _IPEndPointConfig = iPEndPointConfig;

            group = new MultithreadEventLoopGroup();
            bootstrap = new Bootstrap();
            bootstrap.Group(group)
                .Channel<TcpSocketChannel>()
                .Option(ChannelOption.TcpNodelay, true)
                .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                {
                    //管道处理
                    IChannelPipeline pipeline = channel.Pipeline;
                    //下级平台1分钟发送心跳
                    //上级平台是3分钟没有发送就断开连接
                    using (var scope = _serviceProvider.CreateScope())
                    {
                        pipeline.AddLast("jt809MainClientBuffer", new DelimiterBasedFrameDecoder(int.MaxValue,
                             Unpooled.CopiedBuffer(new byte[] { JT809Package.BEGINFLAG }),
                             Unpooled.CopiedBuffer(new byte[] { JT809Package.ENDFLAG })));
                        pipeline.AddLast("jt809MainClientSystemIdleState", new IdleStateHandler(180, 30, 200));
                        pipeline.AddLast("jt809MainClientEncode", scope.ServiceProvider.GetRequiredService<JT809Encoder>());
                        pipeline.AddLast("jt809MainClientDecode", scope.ServiceProvider.GetRequiredService<JT809Decoder>());
                        pipeline.AddLast("jt809MainClientConnection", scope.ServiceProvider.GetRequiredService<JT809MainClientConnectionHandler>());
                        pipeline.AddLast("jt809MainClientServer", scope.ServiceProvider.GetRequiredService<JT809MainClientHandler>());


                    }
                }));
            bootstrap.RemoteAddress(_IPEndPointConfig.iPEndPoint);
        }
        #endregion

        private JT809_0x1001 _jT809_0x1001;
        //private IPEndPoint iPEndPoint;

        #region methods
        public async Task<bool> Login(JT809_0x1001 jT809_0x1001)
        {
            if (disposed) return await Task.FromResult(false);
            //_logger.LogInformation($"ip:{ip},port:{port}");
            _jT809_0x1001 = jT809_0x1001;
            //iPEndPoint= new IPEndPoint(IPAddress.Parse(ip), port);
            bool successed = false;
            try
            {
                if (channel == null)
                {
                    channel = await bootstrap.ConnectAsync(_IPEndPointConfig.iPEndPoint);
                    successed = channel.Open && channel.Active;
                    if (channel.Open && channel.Active)
                    {
                        var package = JT809BusinessType.主链路登录请求消息.Create(_jT809_0x1001);
                        package.Header.MsgGNSSCENTERID = _JT809HeaderOptions.MsgGNSSCENTERID;
                        package.Header.Version = _JT809HeaderOptions.Version;
                        package.Header.EncryptKey = _JT809HeaderOptions.EncryptKey;
                        package.Header.EncryptFlag = _JT809HeaderOptions.EncryptFlag;
                        await channel.WriteAndFlushAsync(new JT809Response(package, 1024));
                        _logger.LogInformation("等待登录应答结果...");
                        _manualResetEvent.Pause();
                    }
                }
            }
            catch (AggregateException ex)
            {
                _logger.LogError(ex.InnerException, $"ip:{_IPEndPointConfig.Ip},port:{_IPEndPointConfig.Port}");
                successed=Login(jT809_0x1001).Result;
                
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"ip:{_IPEndPointConfig.Ip},port:{_IPEndPointConfig.Port}");
                successed = Login(jT809_0x1001).Result;
            }
            return await Task.FromResult(successed);
        }

        public async Task<bool> SendAsync(JT809Response jT809Response)
        {
            bool successed = false;
            if (disposed) return await Task.FromResult(false);
            if (channel == null) throw new NullReferenceException("Channel Not Open");
            if (jT809Response == null) throw new ArgumentNullException("Data is null");
            if (channel.Open && channel.Active)
            {
                _manualResetEvent.Pause();
                await channel.WriteAndFlushAsync(jT809Response);
                successed=true;
            }
            else//重试机制一直重试
            {
                _manualResetEvent.Reset();
                await Policy.HandleResult((channel.Open && channel.Active))
                    .WaitAndRetryForeverAsync(retryAttempt =>
                    {
                        return TimeSpan.FromSeconds(10);
                    }, (exception, timespan, ctx) =>
                     {
                         _logger.LogError($"服务端断开{channel.RemoteAddress}，重试结果{exception.Result}，重试次数{timespan}，下次重试间隔(s){ctx.TotalSeconds}");
                     })
                    .ExecuteAsync(async () =>
                    {
                        channel = await bootstrap.ConnectAsync(_IPEndPointConfig.iPEndPoint);
                        if (channel.Open && channel.Active)
                        {
                            _manualResetEvent.Pause();
                            await channel.WriteAndFlushAsync(jT809Response);
                            successed = true;
                        }
                        //var package = JT809BusinessType.主链路登录请求消息.Create(_jT809_0x1001);
                        //package.Header.MsgGNSSCENTERID = _JT809HeaderOptions.MsgGNSSCENTERID;
                        //package.Header.Version = _JT809HeaderOptions.Version;
                        //package.Header.EncryptKey = _JT809HeaderOptions.EncryptKey;
                        //package.Header.EncryptFlag = _JT809HeaderOptions.EncryptFlag;
                        //await channel.WriteAndFlushAsync(new JT809Response(package, 1024));
                        //var package = JT809BusinessType.主链路登录请求消息.Create(_jT809_0x1001);
                        //await channel.WriteAndFlushAsync(new JT809Response(package, 100));
                        //_logger.LogInformation("尝试重连,等待登录应答结果...");
                        //_manualResetEvent.Pause();
                        return await Task.FromResult(channel.Open && channel.Active);
                    });
            }

            return await Task.FromResult(successed);
        }

        private async Task RunClientAsync()
        {
            await ConnectToServer();
        }

        public async Task ConnectToServer()
        {
            try
            {
                try
                {
                    if (channel == null)
                    {
                        channel = await bootstrap.ConnectAsync(_IPEndPointConfig.iPEndPoint);
                    }
                    if (!(channel.Open && channel.Active))
                    {
                        channel = await bootstrap.ConnectAsync(_IPEndPointConfig.iPEndPoint);
                        if (channel.Open && channel.Active)
                        {
                            Console.WriteLine("cg");
                        }
                    }
                }
                catch (Exception ex)
                {
                    System.Threading.Thread.Sleep(5000);
                    await ConnectToServer();
                }
            }
            finally
            {
                //Console.ReadLine();
                //await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
            }
        }




        private void Dispose(bool disposing)
        {
            if (disposed)
            {
                return;
            }
            if (disposing)
            {
                //清理托管资源
                channel.CloseAsync();
                group.ShutdownGracefullyAsync(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(3));
            }
            //让类型知道自己已经被释放
            disposed = true;
        }
        /// <summary>
        /// 析构
        /// </summary>
        ~JT809MainClient()
        {
            //必须为false
            //这表明，隐式清理时，只要处理非托管资源就可以了。
            Dispose(false);
        }

        public void Dispose()
        {
            //必须为true
            Dispose(true);
            //通知垃圾回收机制不再调用终结器（析构器）
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
